import { MatchCacheService } from 'src/modules/match/match.cache.service';
import { HttpService } from '@nestjs/axios';
import { lastValueFrom } from 'rxjs';
import { MatchGiveUpService } from './services/match-giveup';
import {
  Controller,
  Get,
  Post,
  Body,
  Query,
  UsePipes,
  ValidationPipe,
  HttpException,
  HttpStatus,
  Req,
} from '@nestjs/common';
import { MatchService } from './match.service';
import { CreateMatchDto } from './dto/create-match.dto';
import { UpdateMatchDto } from './dto/update-match.dto';
import { plainToClass } from 'class-transformer';
import {
  GroupStatusEnum,
  MatchPlayerStatusEnum,
  MatchStatusEnum,
  StageStatusEnum,
  stageTypeText,
} from 'src/common/constant/enums';
import {
  ApiBearerAuth,
  ApiBody,
  ApiOperation,
  ApiQuery,
} from '@nestjs/swagger';
import { MatchInfo } from './entities/match.entity';
import { EndMatchDto } from './dto/endMatch.dto';
import { StageService } from 'src/modules/stage/stage.service';
import { Group } from '../group/entities/group.entity';
import { Stage } from '../stage/entities/stage.entity';
import { Category } from '../category/entities/category.entity';
import { Playground } from '../playground/entities/playground.entity';
import { Tournament } from '../tournament/entities/tournament.entity';
import { GameInfo } from '../game/entities/game.entity';
import { Event } from '../event/entities/event.entity';
import { TournamentService } from '../tournament/tournament.service';
import { SearchMatchesDto } from './dto/search-match.dto';
import { GroupService } from '../group/group.service';
import { EventToPlayer } from '../event-to-player/entities/event-to-player.entity';
import logger from 'src/common/utils/log';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { MatchEndService } from './services/match-end';
import { MatchReadyService } from './services/match-ready';
import { Auth } from 'src/common/decorators/auth.decorator';
import { RequirePermission } from 'src/common/decorators/permission.decorator';

type MatchDetailType = Partial<{
  match: Partial<MatchInfo>;
  group: Partial<Group>;
  stage: Partial<Stage>;
  event: Partial<Event>;
  category: Partial<Category>;
  tournament: Partial<Tournament>;
  playground: Partial<Playground>;
  games: Partial<GameInfo>[];
  homePlayerInfo: Partial<EventToPlayer>;
  awayPlayerInfo: Partial<EventToPlayer>;
}>;
type MatchSearchCondition = {
  searchLabel: string;
  searchKey: string;
  options: Array<{
    label: string;
    value: string | number;
  }>;
};

@Auth()
@ApiBearerAuth()
@Controller('match')
export class MatchController {
  constructor(
    private readonly matchService: MatchService,
    private readonly stageService: StageService,
    private readonly tournamentService: TournamentService,
    private readonly groupService: GroupService,
    private eventEmitter: EventEmitter2,
    private readonly matchGiveUpService: MatchGiveUpService,
    private readonly matchEndService: MatchEndService,
    private readonly matchReadyService: MatchReadyService,
    private readonly httpService: HttpService,
    private readonly MatchCacheService: MatchCacheService,
  ) {}

  @Post()
  @UsePipes(new ValidationPipe({ transform: true }))
  create(@Body() createMatchDto: CreateMatchDto) {
    return this.matchService.create(createMatchDto);
  }
  @ApiOperation({ summary: '获取锦标赛比赛列表' })
  @ApiQuery({
    name: 'tournamentId',
    type: Number,
    required: true,
    description: '锦标赛ID',
  })
  @ApiQuery({
    name: 'estimatedStart',
    type: String,
    required: false,
    description: '开始日期，格式如2024-01-01 00:00:00，可为空',
  })
  @ApiQuery({
    name: 'estimatedEnd',
    type: String,
    required: false,
    description: '结束日期，格式如2024-01-31 00:00:00，可为空',
  })
  @RequirePermission('1004001')
  @Get('getTournamentMatches')
  async getTournamentMatches(
    @Query('tournamentId') tournamentId: number,
    @Query('estimatedStart') estimatedStart?: string,
    @Query('estimatedEnd') estimatedEnd?: string,
  ) {
    logger.info(`getTournamentMatches start: tournamentId ${tournamentId}`);
    const matchDataList =
      await this.matchService.getMatchesByTournamentId(tournamentId);
    // const matchDataList =
    //   await this.MatchCacheService.getListByTournamentId(tournamentId);
    logger.info(`getTournamentMatches matchDataList: ${matchDataList.length}`);
    const allMatchList = matchDataList.filter((v) => !v.subMatchId); // 过滤掉子比赛
    logger.info(`getTournamentMatches allMatchList: ${allMatchList.length}`);

    // 解析日期参数`
    const startDate = estimatedStart ? new Date(estimatedStart) : null;
    const endDate = estimatedEnd ? new Date(estimatedEnd) : null;

    // 日期范围过滤函数
    const filterByDateRange = (match: any): boolean => {
      // 如果没有日期参数，则不过滤
      if (!startDate && !endDate) {
        return true;
      }

      // 检查当前比赛是否有日期信息
      if (match.estimatedStart && match.estimatedEnd) {
        const matchStart = new Date(match.estimatedStart);
        const matchEnd = new Date(match.estimatedEnd);

        // 检查比赛日期是否在范围内
        if (startDate && matchEnd < startDate) {
          return false;
        }
        if (endDate && matchStart > endDate) {
          return false;
        }
        return true;
      } else {
        // 如果当前比赛没有日期，但有子比赛，则检查子比赛
        if (match.subMatchInfos && match.subMatchInfos.length > 0) {
          // 检查是否有至少一个子比赛在日期范围内
          const hasValidSubMatch = match.subMatchInfos.some((subMatch) => {
            if (subMatch.estimatedStart && subMatch.estimatedEnd) {
              const subMatchStart = new Date(subMatch.estimatedStart);
              const subMatchEnd = new Date(subMatch.estimatedEnd);

              // 检查子比赛日期是否在范围内
              if (startDate && subMatchEnd < startDate) {
                return false;
              }
              if (endDate && subMatchStart > endDate) {
                return false;
              }
              return true;
            } else {
              // 子比赛也没有日期信息，保留父比赛
              return true;
            }
          });

          // 如果所有子比赛都被过滤，则父比赛也应被过滤
          return hasValidSubMatch;
        }
      }

      // 没有日期信息且没有子比赛的情况，默认保留
      return true;
    };

    // 应用日期范围过滤
    const filteredMatchList = allMatchList.filter(filterByDateRange);
    logger.info(
      `getTournamentMatches filteredMatchList: ${filteredMatchList.length}`,
    );

    const retMatchDataList = filteredMatchList.map((matchData) => {
      const { playground, homePlayer, awayPlayer, ...rest } = matchData;
      const hasByePlayer =
        matchData.homePlayer?.isByePlayer ||
        matchData.awayPlayer?.isByePlayer ||
        false;
      return {
        ...rest,
        hasByePlayer,
        playground: playground
          ? { name: playground.name, id: playground.id }
          : {},
        homePlayerInfo: homePlayer,
        awayPlayerInfo: awayPlayer,
      };
    });
    logger.info(
      `getTournamentMatches retMatchDataList: ${retMatchDataList.length}`,
    );
    return retMatchDataList;
  }

  @RequirePermission('1004001')
  @Get('getTournamentMatchesSimple')
  @ApiOperation({
    summary: '获取锦标赛赛事的简易比赛列表',
    description: '根据锦标赛ID和可选的场地ID，获取相关的简易比赛信息列表',
  })
  @ApiQuery({
    name: 'tournamentId',
    type: Number,
    required: true,
    description: '锦标赛ID，用于筛选特定锦标赛的比赛',
  })
  @ApiQuery({
    name: 'playgroundId',
    type: Number,
    required: false,
    description: '场地ID，可选参数，用于筛选特定场地的比赛',
  })
  async getTournamentMatchesSimple(
    @Query('tournamentId') tournamentId: number,
    @Query('playgroundId') playgroundId?: number,
  ) {
    return await this.matchService.getTournamentMatchesSimple(
      tournamentId,
      playgroundId,
    );
  }
  @Post('saveList')
  @UsePipes(new ValidationPipe({ transform: true }))
  saveList(@Req() req, @Body() matchDataList: UpdateMatchDto[]) {
    const user = req.user;
    const dtoList = matchDataList.map((matchData) => {
      if (matchData.id) {
        matchData.updateBy = user.id;
      } else {
        matchData.createBy = user.id;
        matchData.updateBy = user.id;
      }
      return plainToClass(UpdateMatchDto, matchData);
    });
    return this.matchService.saveList(dtoList);
  }
  @Get('getInProgressMatches')
  @ApiOperation({
    summary: '获取进行中的比赛',
    description: '根据赛事ID获取所有正在进行中的比赛',
  })
  @ApiQuery({
    name: 'tournamentId',
    type: 'string',
    description: '赛事ID',
  })
  async getInProgressMatches(
    @Query('tournamentId') tournamentId: string | number,
  ): Promise<Array<MatchInfo>> {
    return await this.matchService.getInProgressMatches(
      parseInt(tournamentId + ''),
    );
  }
  @Get('pushInProgressMatches')
  @ApiOperation({
    summary: '推送进行中的比赛',
    description: '根据赛事ID获取所有正在进行中的比赛并推送给大屏',
  })
  @ApiQuery({
    name: 'tournamentId',
    type: 'string',
    description: '赛事ID',
  })
  async pushInProgressMatches(
    @Query('tournamentId') tournamentId: string | number,
  ) {
    this.eventEmitter.emit('match.playMatchUpdate', tournamentId);
  }

  @Get('getMatchDetail')
  @ApiOperation({
    summary: '获取比赛详情',
    description: '根据比赛ID获取比赛的详细信息',
  })
  @ApiQuery({
    name: 'matchId',
    type: 'string',
    description: '比赛ID',
  })
  async getMatchDetail(
    @Query('matchId') matchId: string | number,
  ): Promise<MatchDetailType> {
    const retMatchData = await this.matchService.getMatchDetail(
      parseInt(matchId + ''),
    );
    const { playground, games, ...matchRest } = retMatchData;
    return {
      match: matchRest,
      playground,
      games,
      homePlayerInfo: retMatchData.homePlayer,
      awayPlayerInfo: retMatchData.awayPlayer,
    };
  }
  @RequirePermission(['1004002', '1004003'], 'OR')
  @Post('ready')
  @UsePipes(new ValidationPipe({ transform: true }))
  @ApiOperation({
    summary: 'ready比赛(废弃，请切换到/match/readyMatch)',
  })
  async readyMatch(@Req() req, @Body() matchDataList: UpdateMatchDto[]) {
    // 获取基础URL和新接口地址
    const baseUrl = req.protocol + '://' + req.get('host');
    const newUrl = `${baseUrl}/match/readyMatch`;

    try {
      // 转发请求到新接口
      const response = await lastValueFrom(
        this.httpService.post(newUrl, matchDataList, {
          headers: req.headers,
        }),
      );
      // 返回新接口的响应
      return response.data;
    } catch (error) {
      // 转发失败时的错误处理
      console.error('转发请求失败:', error);
      throw new HttpException('转发请求失败', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  @Get('start')
  async startMatch(@Req() req, @Query('matchId') matchId: number) {
    // TODO: 未检查选手是否都已到齐
    const user = req.user;
    const matchInfo = await this.matchService.findById(matchId);
    // if (
    //   matchInfo.homePlayerStatus === MatchPlayerStatusEnum.NotCheckIn ||
    //   matchInfo.awayPlayerStatus === MatchPlayerStatusEnum.NotCheckIn
    // ) {
    //   throw new HttpException(
    //     `参赛选手并为全部签到 (${matchInfo.homePlayerStatus === MatchPlayerStatusEnum.NotCheckIn ? matchInfo.homePlayer : matchInfo.awayPlayer})`,
    //     HttpStatus.INTERNAL_SERVER_ERROR,
    //   );
    // }
    if (matchInfo.status >= MatchStatusEnum.InProgress) {
      throw new HttpException(
        `比赛当前状态已经开始或者结束`,
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
    await this.matchService.update(parseInt(matchId as any), {
      status: MatchStatusEnum.InProgress,
      actualStart: new Date(),
      homePlayerRally: 0,
      awayPlayerRally: 0,
      updateBy: user.id,
    });
    logger.info(`startMatch update match success ${matchId}`);
    // 如果是团队赛更新其付比赛
    await this.matchService.updateParentMatch(parseInt(matchId as any), {
      status: MatchStatusEnum.InProgress,
    });
    logger.info(`startMatch updateParentMatch success ${matchId}`);
    // 更新group/stage状态
    await this.startStage(matchInfo);
  }

  @RequirePermission(['1007001', '1007002', '1007003'], 'OR')
  @Post('end')
  @UsePipes(new ValidationPipe({ transform: true }))
  @ApiOperation({
    summary: '结束比赛(废弃，请切换到/match/endMatch)',
  })
  @ApiBody({
    type: EndMatchDto,
  })
  async endMatch(
    @Req() req,
    @Body()
    endMatchDto: EndMatchDto,
  ) {
    try {
      // 记录转发日志
      logger.info(`转发请求: /match/end -> /common/getMatchEnd`);

      // 构造新的请求URL，这里假设新接口在相同域名下的/common/getMatchEnd路径
      const baseUrl = req.protocol + '://' + req.get('host');
      const newUrl = `${baseUrl}/match/endMatch?matchId=${endMatchDto.id}&homePlayerRally=${endMatchDto.homePlayerRally}&awayPlayerRally=${endMatchDto.awayPlayerRally}`;

      // 使用HttpService转发请求，保留原始请求头和请求体
      // 注意：根据实际情况调整URL和请求配置
      const response = await lastValueFrom(
        this.httpService.post(newUrl, endMatchDto, {
          headers: req.headers,
        }),
      );

      // 返回新接口的响应
      return response.data;
    } catch (error) {
      // 错误处理
      logger.error('转发请求失败:', error);
      throw new HttpException('转发请求失败', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @Get('player/checkIn')
  @ApiOperation({
    summary: '选手签到/弃权',
  })
  @ApiQuery({
    name: 'matchId',
    type: 'number',
    description: '比赛ID',
  })
  @ApiQuery({
    name: 'registrationIndex',
    type: 'string',
    description: '选手报名序号(不是选手userId)',
  })
  @ApiQuery({
    name: 'status',
    type: 'number',
    enum: MatchPlayerStatusEnum,
    description: '更新的选手状态',
  })
  async playerCheckIn(
    @Query('matchId') matchId: string,
    @Query('registrationIndex') registrationIndex: string,
  ) {
    console.log('checkIn', matchId, registrationIndex);
    const newMathData = await this.updatePlayerStatus(
      matchId,
      registrationIndex,
      MatchPlayerStatusEnum.CheckedIn,
    );
    await this.matchService.update(parseInt(matchId), newMathData);
  }

  @RequirePermission(['1007001', '1007002', '1007003'], 'OR')
  @Get('player/giveUp')
  @ApiOperation({
    summary: '选手放弃比赛（废弃，请切换到/match/playerGiveUp）',
  })
  @ApiQuery({
    name: 'matchId',
    type: 'number',
    description: '比赛ID',
  })
  @ApiQuery({
    name: 'playerId',
    type: 'number',
    description: '选手playerId',
  })
  @ApiQuery({
    name: 'type',
    type: 'string',
    enum: ['team', 'player'],
    description: '放弃的类型',
  })
  async playerGiveUp(
    @Req() req,
    @Query('matchId') matchId: string,
    @Query('playerId') playerId: string,
    @Query('type') type: 'team' | 'player',
  ) {
    // 方案1: 如果新接口已经存在，可以使用重定向
    const baseUrl = req.protocol + '://' + req.get('host');
    const newUrl = `${baseUrl}/match/playerGiveUp?matchId=${matchId}&playerId=${playerId}&type=${type}`;

    // 获取当前请求的headers
    const headers = req.headers;

    // 转发请求到新接口并返回结果
    try {
      const response = await lastValueFrom(
        this.httpService.get(newUrl, { headers }),
      );
      return response.data;
    } catch {
      throw new HttpException(
        `请切换到/match/playerGiveUp`,
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
  // 获取并更新比赛选手状态，但是不更新数据库，只把更新的数据返回
  private async updatePlayerStatus(
    matchId: string,
    registrationIndex: string,
    newStatus: MatchPlayerStatusEnum,
  ) {
    console.log(matchId, registrationIndex);
    const matchInfo = await this.matchService.findById(parseInt(matchId));
    const { homePlayer, awayPlayer, playgroundId } = matchInfo;
    if (!playgroundId) {
      // throw new HttpException(
      //   `该场比赛未指定比赛场地`,
      //   HttpStatus.INTERNAL_SERVER_ERROR,
      // );
    }
    if (homePlayer.registrationIndex === registrationIndex) {
      // home player
      matchInfo.homePlayerStatus = newStatus;
    } else if (awayPlayer.registrationIndex === registrationIndex) {
      matchInfo.awayPlayerStatus = newStatus;
    } else {
      throw new HttpException(
        `registrationIndex ${registrationIndex} not found in match ${matchId}`,
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
    return matchInfo;
  }
  // 检查当前比赛stage是否结束，如果结束，则更新下一轮比赛的参赛人
  private async startStage(matchDetail: MatchInfo) {
    const stageInfo = await this.stageService.getByMatchId(matchDetail.id);
    if (!stageInfo) {
      logger.info(`startStage: stageInfo not found ${matchDetail.id}`);
      return;
    }
    const { groups } = stageInfo;
    const groupInfo = groups.find((v) => {
      return v.id === matchDetail.groupId;
    });
    if (stageInfo.status !== StageStatusEnum.InProgress) {
      logger.info(`startStage: stageInfo:${stageInfo.id} ${stageInfo.status}`);
      this.stageService.update(stageInfo.id, {
        status: StageStatusEnum.InProgress,
      });
    }
    if (groupInfo.status !== GroupStatusEnum.InProgress) {
      logger.info(`startStage: groupInfo:${groupInfo.id} ${groupInfo.status}`);
      this.groupService.update(groupInfo.id, {
        status: GroupStatusEnum.InProgress,
      });
    }
  }
  @Get('getMatchSearchCondition')
  @ApiOperation({
    summary: '获取比赛搜索条件',
    description: '获取比赛搜索条件',
  })
  async getMatchSearchCondition(
    @Query('tournamentId') tournamentId: number,
  ): Promise<MatchSearchCondition[]> {
    const stageOrderInEvent = {
      searchLabel: '阶段',
      searchKey: 'stageOrderInEvent',
      options: [
        { label: '第一阶段', value: 1 },
        { label: '第二阶段', value: 2 },
        { label: '第三阶段', value: 3 },
        { label: '第四阶段', value: 4 },
        { label: '第五阶段', value: 5 },
        { label: '第六阶段', value: 6 },
        { label: '第七阶段', value: 7 },
      ],
    };
    const groupLoopIndexType = {
      searchLabel: '轮次',
      searchKey: 'groupLoopIndexType',
      options: [
        { label: '第一轮', value: 1 },
        { label: '第二轮', value: 2 },
        { label: '第三轮', value: 3 },
        { label: '第四轮', value: 4 },
        { label: '第五轮', value: 5 },
        { label: '第六轮', value: 6 },
        { label: '第七轮', value: 7 },
      ],
    };
    const statusType: MatchSearchCondition = {
      searchLabel: '状态',
      searchKey: 'statusType',
      options: [
        { label: '已创建', value: MatchStatusEnum.Created },
        { label: '待开赛', value: MatchStatusEnum.Ready },
        { label: '进行中', value: MatchStatusEnum.InProgress },
        { label: '已结束', value: MatchStatusEnum.Finished },
      ],
    };
    const scheduleType: MatchSearchCondition = {
      searchLabel: '编排',
      searchKey: 'scheduleType',
      options: [
        { label: '未编排', value: 0 },
        { label: '已编排', value: 1 },
      ],
    };
    const stageType: MatchSearchCondition = {
      searchLabel: '类型',
      searchKey: 'stageType',
      options: Object.entries(stageTypeText).map(([key, value]) => ({
        label: value,
        value: parseInt(key),
      })),
    };
    const eventCondition: MatchSearchCondition = {
      searchLabel: '组别',
      searchKey: 'eventId',
      options: [],
    };
    const tournamentDetail =
      await this.tournamentService.findDetailById(tournamentId);
    const { categories } = tournamentDetail;
    categories.forEach((category) => {
      const { events, name: categoryName } = category;
      events.forEach((event) => {
        const { name: eventName, id: eventId } = event;
        eventCondition.options.push({
          label: `${categoryName} ${eventName}`,
          value: eventId,
        });
      });
    });
    return [
      eventCondition,
      stageType,
      scheduleType,
      statusType,
      groupLoopIndexType,
      stageOrderInEvent,
    ];
  }
  @Post('searchMatches')
  @UsePipes(new ValidationPipe({ transform: true }))
  @ApiOperation({ summary: '搜索比赛', description: '根据多个条件搜索比赛' })
  @ApiBody({ type: SearchMatchesDto })
  async searchMatches(
    @Body() searchDto: SearchMatchesDto,
  ): Promise<MatchInfo[]> {
    const matchDataList =
      await this.matchService.handleGetMatchesByConditions(searchDto);
    const allMatchList = [];
    matchDataList.forEach((matchData) => {
      if (matchData.isTeamMatch && matchData.subMatchInfos.length) {
        matchData.subMatchInfos.forEach((subMatchInfo) => {
          subMatchInfo.group = matchData.group;
          subMatchInfo.playground = matchData.playground;
          allMatchList.push(subMatchInfo);
        });
      } else {
        allMatchList.push(matchData);
      }
    });

    const retMatchDataList = allMatchList.map((matchData) => {
      const { group, playground, ...rest } = matchData;
      const { stage } = group;
      const { event } = stage;
      // const { eventToPlayers } = event;
      // const { category } = event;
      // const { tournament } = category;
      return {
        ...rest,
        group: { name: group.name, id: group.id },
        stage: { name: stage.name, id: stage.id, type: stage.type },
        event: { name: event.name, id: event.id, type: event.type },
        // category: { name: category.name, id: category.id },
        // tournament: { name: tournament.name, id: tournament.id },
        playground: playground
          ? { name: playground.name, id: playground.id }
          : {},
        homePlayerInfo: matchData.homePlayer,
        awayPlayerInfo: matchData.awayPlayer,
      };
    });
    return retMatchDataList;
  }
  @Get('restartMatch')
  @ApiOperation({
    summary: '重新开始比赛',
    description: '重新开始比赛',
  })
  async restartMatch(@Query('matchId') matchId: number): Promise<MatchInfo> {
    const matchInfo = await this.matchService.getMatchDetail(matchId);
    const clearMatchData = (matchData) => {
      matchData.status = MatchStatusEnum.Ready;
      matchData.actualStart = null;
      matchData.actualEnd = null;
      matchData.homePlayerRally = 0;
      matchData.awayPlayerRally = 0;
      matchData.winnerId = null;
      matchData.games = [];
    };
    if (matchInfo.subMatchInfos && matchInfo.subMatchInfos.length) {
      matchInfo.subMatchInfos.forEach((subMatchInfo) => {
        clearMatchData(subMatchInfo);
      });
      clearMatchData(matchInfo);
    } else {
      clearMatchData(matchInfo);
    }
    const res = await this.matchService.save(matchInfo);
    return res;
  }
}
